//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
/*
#------------------------------------------------------------------------------
#    Revision History
#    Date          	   Author         	     Activity ID     		     Activity Headline
#    2007-03-19    zuoxiaona   	     CEDB200047228       Submit STK CODE -RIL- lampson 
#------------------------------------------------------------------------------
*/
/*
------------------------------------------------------------------------------
    Revision History
    Date                Author            Activity ID            Activity Headline
    2007-04-06          gaorenhui         CEDB200050585          Add +CPI to indicate call progress information
------------------------------------------------------------------------------
*/

#include "precomp.h"

//
// NOTE: all the following mapping arrays must be kept sorted in the left column
//       (this is needed, because they're used for binary search)
//

//
// Call management commands
//
static const LISTVALUEMAP g_rglvmCapsCallCmds[] =
{
    { 0,    RIL_CAPS_CALLCMD_RELEASEHELD              },
    { 1,    RIL_CAPS_CALLCMD_RELEASEACTIVE_ACCEPTHELD },
    { 2,    RIL_CAPS_CALLCMD_HOLDACTIVE_ACCEPTHELD    },
    { 3,    RIL_CAPS_CALLCMD_ADDHELDTOCONF            },
    { 4,    RIL_CAPS_CALLCMD_ADDHELDTOCONF_DISCONNECT },
    { 5,    RIL_CAPS_CALLCMD_INVOKECCBS               },
    { 11,   RIL_CAPS_CALLCMD_RELEASECALL              },
    { 12,   RIL_CAPS_CALLCMD_RELEASECALL              },
    { 13,   RIL_CAPS_CALLCMD_RELEASECALL              },
    { 14,   RIL_CAPS_CALLCMD_RELEASECALL              },
    { 15,   RIL_CAPS_CALLCMD_RELEASECALL              },
    { 16,   RIL_CAPS_CALLCMD_RELEASECALL              },
    { 17,   RIL_CAPS_CALLCMD_RELEASECALL              },
    { 18,   RIL_CAPS_CALLCMD_RELEASECALL              },
    { 19,   RIL_CAPS_CALLCMD_RELEASECALL              },
    { 21,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
    { 22,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
    { 23,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
    { 24,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
    { 25,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
    { 26,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
    { 27,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
    { 28,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
    { 29,   RIL_CAPS_CALLCMD_HOLDALLBUTONE            },
};
#define NUM_CAPSCALLCMDS    (sizeof(g_rglvmCapsCallCmds) / sizeof(LISTVALUEMAP))


//
// Bearer service connection speeds (first set)
//
static const LISTVALUEMAP g_rglvmCapsConnSpeeds1[] =
{
    { 0,    RIL_CAPS_SPEED1_AUTO        },
    { 1,    RIL_CAPS_SPEED1_300_V21     },
    { 2,    RIL_CAPS_SPEED1_1200_V22    },
    { 3,    RIL_CAPS_SPEED1_1200_75_V23 },
    { 4,    RIL_CAPS_SPEED1_2400_V22BIS },
    { 5,    RIL_CAPS_SPEED1_2400_V26TER },
    { 6,    RIL_CAPS_SPEED1_4800_V32    },
    { 7,    RIL_CAPS_SPEED1_9600_V32    },
    { 12,   RIL_CAPS_SPEED1_9600_V34    },
    { 14,   RIL_CAPS_SPEED1_14400_V34   },
    { 15,   RIL_CAPS_SPEED1_19200_V34   },
    { 16,   RIL_CAPS_SPEED1_28800_V34   },
    { 34,   RIL_CAPS_SPEED1_1200_V120   },
    { 36,   RIL_CAPS_SPEED1_2400_V120   },
    { 38,   RIL_CAPS_SPEED1_4800_V120   },
    { 39,   RIL_CAPS_SPEED1_9600_V120   },
    { 43,   RIL_CAPS_SPEED1_14400_V120  },
    { 47,   RIL_CAPS_SPEED1_19200_V120  },
    { 48,   RIL_CAPS_SPEED1_28800_V120  },
    { 49,   RIL_CAPS_SPEED1_38400_V120  },
    { 50,   RIL_CAPS_SPEED1_48000_V120  },
    { 65,   RIL_CAPS_SPEED1_300_V110    },
    { 66,   RIL_CAPS_SPEED1_1200_V110   },
    { 68,   RIL_CAPS_SPEED1_2400_V110   },
    { 70,   RIL_CAPS_SPEED1_4800_V110   },
    { 71,   RIL_CAPS_SPEED1_9600_V110   },
    { 75,   RIL_CAPS_SPEED1_14400_V110  },
    { 79,   RIL_CAPS_SPEED1_19200_V110  },
    { 80,   RIL_CAPS_SPEED1_28800_V110  },
    { 81,   RIL_CAPS_SPEED1_38400_V110  },
    { 82,   RIL_CAPS_SPEED1_48000_V110  },
    { 83,   RIL_CAPS_SPEED1_56000_V110  },
};
#define NUM_CAPSCONNSPEEDS1 (sizeof(g_rglvmCapsConnSpeeds1) / sizeof(LISTVALUEMAP))


//
// Bearer service connection speeds (second set)
//
static const LISTVALUEMAP g_rglvmCapsConnSpeeds2[] =
{
    { 51,   RIL_CAPS_SPEED2_56000_V120   },
    { 115,  RIL_CAPS_SPEED2_56000_TRANSP },
    { 116,  RIL_CAPS_SPEED2_64000_TRANSP },
    { 120,  RIL_CAPS_SPEED2_32000_PIAFS32K },
    { 121,  RIL_CAPS_SPEED2_64000_PIAFS64K },
    { 130,  RIL_CAPS_SPEED2_28800_MULTIMEDIA },
    { 131,  RIL_CAPS_SPEED2_32000_MULTIMEDIA },
    { 132,  RIL_CAPS_SPEED2_33600_MULTIMEDIA },
    { 133,  RIL_CAPS_SPEED2_56000_MULTIMEDIA },
    { 134,  RIL_CAPS_SPEED2_64000_MULTIMEDIA },
};
#define NUM_CAPSCONNSPEEDS2 (sizeof(g_rglvmCapsConnSpeeds2) / sizeof(LISTVALUEMAP))


//
// Bearer service names
//
static const LISTVALUEMAP g_rglvmCapsSvcNames[] =
{
    { 0,    RIL_CAPS_BSVCNAME_DATACIRCUIT_ASYNC_UDI_MODEM },
    { 1,    RIL_CAPS_BSVCNAME_DATACIRCUIT_SYNC_UDI_MODEM  },
    { 2,    RIL_CAPS_BSVCNAME_PADACCESS_ASYNC_UDI         },
    { 3,    RIL_CAPS_BSVCNAME_PACKETACCESS_SYNC_UDI       },
    { 4,    RIL_CAPS_BSVCNAME_DATACIRCUIT_ASYNC_RDI       },
    { 5,    RIL_CAPS_BSVCNAME_DATACIRCUIT_SYNC_RDI        },
    { 6,    RIL_CAPS_BSVCNAME_PADACCESS_ASYNC_RDI         },
    { 7,    RIL_CAPS_BSVCNAME_PACKETACCESS_SYNC_RDI       },
};
#define NUM_CAPSSVCNAMES    (sizeof(g_rglvmCapsSvcNames) / sizeof(LISTVALUEMAP))


//
// Bearer service connection elements
//
static const LISTVALUEMAP g_rglvmCapsConnElems[] =
{
    { 0,    RIL_CAPS_BSVCCE_TRANSPARENT         },
    { 1,    RIL_CAPS_BSVCCE_NONTRANSPARENT      },
    { 2,    RIL_CAPS_BSVCCE_BOTH_TRANSPARENT    },
    { 3,    RIL_CAPS_BSVCCE_BOTH_NONTRANSPARENT },
};
#define NUM_CAPSCONNELEMS   (sizeof(g_rglvmCapsConnElems) / sizeof(LISTVALUEMAP))


//
// Equipment states
//
static const LISTVALUEMAP g_rglvmCapsEqStates[] =
{
    { 0,    RIL_CAPS_EQSTATE_MINIMUM        },
    { 1,    RIL_CAPS_EQSTATE_FULL           },
    { 2,    RIL_CAPS_EQSTATE_DISABLETX      },
    { 3,    RIL_CAPS_EQSTATE_DISABLERX      },
    { 4,    RIL_CAPS_EQSTATE_DISABLETXANDRX },
};
#define NUM_CAPSEQSTATES    (sizeof(g_rglvmCapsEqStates) / sizeof(LISTVALUEMAP))


//
// Phonebook storage locations
//
// NOTE: This is a sorted list!!! It is necessary to keep the szString
// fields sorted in alphabetical order.
//
static const LISTSTRINGMAP g_rglsmCapsPBLocations[] =
{
    { "EN",   RIL_CAPS_PBLOC_SIMEMERGENCY   },
    { "FD",   RIL_CAPS_PBLOC_SIMFIXDIALING  },
    { "LD",   RIL_CAPS_PBLOC_SIMLASTDIALING },
#ifdef OEM1_DRIVER
    { "MT",   RIL_CAPS_PBLOC_SIMPHONEBOOK   },           // RIL_PBLOC_SIMPHONEBOOK (MT is used for TI phonebook)
#endif
    { "ON",   RIL_CAPS_PBLOC_OWNNUMBERS     },
#if !defined(OEM1_DRIVER)
    { "SM",   RIL_CAPS_PBLOC_SIMPHONEBOOK   },		// Used by OEM2 and Wavecom
#endif
};
#define NUM_CAPSPBLOCS      (sizeof(g_rglsmCapsPBLocations) / sizeof(LISTSTRINGMAP))


//
// Messaging service types
//
static const LISTVALUEMAP g_rglvmCapsMsgSvcTypes[] =
{
    { 0,    RIL_CAPS_MSGSVCTYPE_PHASE2     },
    { 1,    RIL_CAPS_MSGSVCTYPE_PHASE2PLUS },
};
#define NUM_CAPSMSGSVCTYPES (sizeof(g_rglvmCapsMsgSvcTypes) / sizeof(LISTVALUEMAP))


//
// Message storage locations
//
static const LISTSTRINGMAP g_rglsmCapsMsgLocations[] =
{
    { "BM",   RIL_CAPS_MSGLOC_BROADCAST    },
    { "BR",   RIL_CAPS_MSGLOC_STATUSREPORT },
    { "SM",   RIL_CAPS_MSGLOC_SIM          },
};
#define NUM_CAPSMSGLOCS     (sizeof(g_rglsmCapsMsgLocations) / sizeof(LISTSTRINGMAP))


//
// Message status values
//
static const LISTVALUEMAP g_rglvmCapsMsgStatusValues[] =
{
    { 0,    RIL_CAPS_MSGSTATUS_RECUNREAD },
    { 1,    RIL_CAPS_MSGSTATUS_RECREAD   },
    { 2,    RIL_CAPS_MSGSTATUS_STOUNSENT },
    { 3,    RIL_CAPS_MSGSTATUS_STOSENT   },
};
#define NUM_CAPSMSGSTATUSVALUES (sizeof(g_rglvmCapsMsgStatusValues) / sizeof(LISTVALUEMAP))


//
// Locking facilities
//
static const LISTSTRINGMAP g_rglsmCapsLockFacilities[] =
{
    { "CS",   RIL_CAPS_LOCKFACILITY_CNTRL           },
    { "FD",   RIL_CAPS_LOCKFACILITY_SIM_FIXEDIALING },
    { "P2",   RIL_CAPS_LOCKFACILITY_SIM_PIN2        },
    { "PC",   RIL_CAPS_LOCKFACILITY_CORPPERS        },
    { "PF",   RIL_CAPS_LOCKFACILITY_PH_FSIM         },
    { "PN",   RIL_CAPS_LOCKFACILITY_NETWORKPERS     },
    { "PP",   RIL_CAPS_LOCKFACILITY_SERVICEPROVPERS },
    { "PS",   RIL_CAPS_LOCKFACILITY_PH_SIM          },
    { "PU",   RIL_CAPS_LOCKFACILITY_NETWORKSUBPERS  },
    { "SC",   RIL_CAPS_LOCKFACILITY_SIM             },
};
#define NUM_CAPSLOCKFACILITIES  (sizeof(g_rglsmCapsLockFacilities) / sizeof(LISTSTRINGMAP))


//
// Types of call barring
//
static const LISTSTRINGMAP g_rglsmCapsBarrTypes[] =
{
    { "AB",   RIL_CAPS_BARRTYPE_ALLBARRING          },
    { "AC",   RIL_CAPS_BARRTYPE_ALLINCOMINGBARRING  },
    { "AG",   RIL_CAPS_BARRTYPE_ALLOUTGOINGBARRING  },
    { "AI",   RIL_CAPS_BARRTYPE_ALLINCOMING         },
    { "AO",   RIL_CAPS_BARRTYPE_ALLOUTGOING         },
    { "IR",   RIL_CAPS_BARRTYPE_INCOMINGROAMING     },
    { "NS",   RIL_CAPS_BARRTYPE_INCOMINGNOTINSIM    },
    { "OI",   RIL_CAPS_BARRTYPE_OUTGOINGINT         },
    { "OX",   RIL_CAPS_BARRTYPE_OUTGOINGINTEXTOHOME },
};
#define NUM_CAPSBARRTYPES   (sizeof(g_rglsmCapsBarrTypes) / sizeof(LISTSTRINGMAP))

//
// Forwarding reasons
//
static const LISTVALUEMAP g_rglvmCapsFwdReasons[] =
{
    { 0,    RIL_CAPS_FWDREASON_UNCONDITIONAL  },
    { 1,    RIL_CAPS_FWDREASON_MOBILEBUSY     },
    { 2,    RIL_CAPS_FWDREASON_NOREPLY        },
    { 3,    RIL_CAPS_FWDREASON_UNREACHABLE    },
    { 4,    RIL_CAPS_FWDREASON_ALLFORWARDING  },
    { 5,    RIL_CAPS_FWDREASON_ALLCONDITIONAL },
};
#define NUM_CAPSFWDREASONS  (sizeof(g_rglvmCapsFwdReasons) / sizeof(LISTVALUEMAP))

//
// HSCSD channel codings
//
static const DWORD g_rgdwCapsHSCSDCodings[] =
{
    RIL_CAPS_HSCSDCODING_4800_FULLRATE,      // 1 = 2^0
    0,                                       // 2 = 2^1
    RIL_CAPS_HSCSDCODING_9600_FULLRATE,      // 4 = 2^2
    RIL_CAPS_HSCSDCODING_14400_FULLRATE,     // 8 = 2^3
};
#define NUM_CAPSHSCSDCODINGS    (sizeof(g_rgdwCapsHSCSDCodings) / sizeof(DWORD))

//
// HSCSD air interface user rates
//
static const LISTVALUEMAP g_rglvmHSCSDAirRates[] =
{
    { 0,    RIL_HSCSDAIURATE_UNKNOWN    },
    { 1,    RIL_HSCSDAIURATE_9600       },
    { 2,    RIL_HSCSDAIURATE_14400      },
    { 3,    RIL_HSCSDAIURATE_19200      },
    { 4,    RIL_HSCSDAIURATE_28800      },
    { 5,    RIL_HSCSDAIURATE_38400      },
    { 6,    RIL_HSCSDAIURATE_43200      },
    { 7,    RIL_HSCSDAIURATE_57600      },
};
#define NUM_CAPSHSCSDAIRRATES   (sizeof(g_rglvmHSCSDAirRates) / sizeof(LISTVALUEMAP))



//
//
//
static HRESULT ParseFakeDWord(const DWORD dwValue, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseFakeDWord);
    DWORD* pdw = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdw = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdw) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    *pdw = dwValue;

    pBlob = (void*)pdw;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdw);
    }
    return hr;
}


//
//
//
static HRESULT ParseRangeRsp(const LPCSTR szCmd, const BOOL fIgnoreRest, LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseRangeRsp);
    BOOL fParsedPostfix;
    RILRANGE* prr = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prr = (RILRANGE*)AllocBlob(sizeof(RILRANGE));
    if (!prr) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prr, 0x00, sizeof(RILRANGE));

    // Parse "<prefix><command><range><CR><LF>"
    if (!ParseRspPrefix(szRsp, szRsp)              ||
        !MatchStringBeginning(szRsp, szCmd, szRsp) ||
        !ParseRange(szRsp, *prr, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<postfix>" (skipping the rest of the response, if necessary)
    if (fIgnoreRest) {
        fParsedPostfix = FindRspPostfix(szRsp, szRsp);
    } else {
        fParsedPostfix = ParseRspPostfix(szRsp, szRsp);
    }
    if (!fParsedPostfix) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prr;
    cbBlob = sizeof(RILRANGE);

Error:
    if (FAILED(hr)) {
        FreeBlob(prr);
    }
    return hr;
}


//
//
//
static HRESULT ParseRangeListRsp(const LPCSTR szCmd, const LISTVALUEMAP* const rglvmMapArray, const UINT nMapArrayLength,
                                 const BOOL fIgnoreRest, LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseRangeListRsp);
    BOOL fParsedPostfix;
    DWORD* pdw = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdw = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdw) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdw, 0x00, sizeof(DWORD));

    // Parse "<prefix><command><list><CR><LF>"
    if (!ParseRspPrefix(szRsp, szRsp)              ||
        !MatchStringBeginning(szRsp, szCmd, szRsp) ||
        !ParseRangeList(szRsp, rglvmMapArray, nMapArrayLength, *pdw, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<postfix>" (skipping the rest of the response, if necessary)
    if (fIgnoreRest) {
        fParsedPostfix = FindRspPostfix(szRsp, szRsp);
    } else {
        fParsedPostfix = ParseRspPostfix(szRsp, szRsp);
    }
    if (!fParsedPostfix) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)pdw;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdw);
    }
    return hr;
}


//
//
//
static HRESULT ParseStringListRsp(const LPCSTR szCmd, const LISTSTRINGMAP* const rglsmMapArray, const UINT nMapArrayLength,
                                  LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseStringListRsp);
    DWORD* pdw = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdw = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdw) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdw, 0x00, sizeof(DWORD));

    // Parse "<prefix><command><list><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                        ||
        !MatchStringBeginning(szRsp, szCmd, szRsp)                           ||
        !ParseStringList(szRsp, rglsmMapArray, nMapArrayLength, *pdw, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)pdw;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdw);
    }
    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsDial(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsDial);
#if defined(WAVECOM_DRIVER) || defined(EMP_DRIVER)
    // HW-SPECIFIC: This uses WaveCom-specific data, adding EMP

    RILCAPSDIAL* prcd = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcd = (RILCAPSDIAL*)AllocBlob(sizeof(RILCAPSDIAL));
    if (!prcd) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcd, 0x00, sizeof(RILCAPSDIAL));
    prcd->cbSize = sizeof(RILCAPSDIAL);

    prcd->dwCallTypes = RIL_CAPS_CALLTYPE_VOICE | RIL_CAPS_CALLTYPE_DATA | RIL_CAPS_CALLTYPE_PTT | RIL_CAPS_CALLTYPE_VT;
    prcd->dwOptions = RIL_CAPS_DIALOPT_RESTRICTID | RIL_CAPS_DIALOPT_PRESENTID;
    prcd->dwParams = RIL_PARAM_CD_CALLTYPES | RIL_PARAM_CD_OPTIONS;

    pBlob = (void*)prcd;
    cbBlob = sizeof(RILCAPSDIAL);

Error:
    if (FAILED(hr)) {
        FreeBlob(prcd);
    }
#else  // WAVECOM_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // WAVECOM_DRIVER

    return hr;
}



//
//
//
static HRESULT ParseGetDevCapsDTMFDurationRange(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsDTMFDurationRange);
    return ParseRangeRsp("+VTD: ", FALSE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsCallMgtCmds(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsCallMgtCmds);
    return ParseRangeListRsp("+CHLD: ", g_rglvmCapsCallCmds, NUM_CAPSCALLCMDS, FALSE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsBearerSvc(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsBearerSvc);
    RILCAPSBEARERSVC* prcbs = NULL;
    LPCSTR szDummy;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcbs = (RILCAPSBEARERSVC*)AllocBlob(sizeof(RILCAPSBEARERSVC));
    if (!prcbs) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcbs, 0x00, sizeof(RILCAPSBEARERSVC));
    prcbs->cbSize = sizeof(RILCAPSBEARERSVC);

    // Parse "<prefix>+CBST: <speed_list>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                                  ||
        !MatchStringBeginning(szRsp, "+CBST: ", szRsp)                                                 ||
        !ParseRangeList(szRsp, g_rglvmCapsConnSpeeds1, NUM_CAPSCONNSPEEDS1, prcbs->dwSpeeds1, szDummy) ||
        !ParseRangeList(szRsp, g_rglvmCapsConnSpeeds2, NUM_CAPSCONNSPEEDS2, prcbs->dwSpeeds2, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prcbs->dwParams |= (RIL_PARAM_CBS_SPEEDS1 | RIL_PARAM_CBS_SPEEDS2);

    // Parse ",<svcname_list>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseRangeList(szRsp, g_rglvmCapsSvcNames, NUM_CAPSSVCNAMES, prcbs->dwServiceNames, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prcbs->dwParams |= RIL_PARAM_CBS_SERVICENAMES;

    // Parse ",<connelem_list><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)                                                            ||
        !ParseRangeList(szRsp, g_rglvmCapsConnElems, NUM_CAPSCONNELEMS, prcbs->dwConnectionElements, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prcbs->dwParams |= RIL_PARAM_CBS_CONNECTIONELEMENTS;

    pBlob = (void*)prcbs;
    cbBlob = sizeof(RILCAPSBEARERSVC);

Error:
    if (FAILED(hr)) {
        FreeBlob(prcbs);
    }
    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsRLP(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsRLP);
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCAPSRLP* rgrcrlp = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "+CRLP: "
    while (MatchStringBeginning(szRsp, "+CRLP: ", szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrcrlp, sizeof(RILCAPSRLP), nUsed, &nAllocated, CAPS_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrcrlp[nUsed], 0x00, sizeof(RILCAPSRLP));
        rgrcrlp[nUsed].cbSize = sizeof(RILCAPSRLP);

        // Parse "<iws_range>"
        if (!ParseRange(szRsp, rgrcrlp[nUsed].rrIWSRange, szRsp)) {
            goto Continue;
        }
        rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_IWSRANGE;

        // Parse ",<mws_range>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseRange(szRsp, rgrcrlp[nUsed].rrMWSRange, szRsp)) {
            goto Continue;
        }
        rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_MWSRANGE;

        // Parse ",<acktimer_range>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseRange(szRsp, rgrcrlp[nUsed].rrAckTimerRange, szRsp)) {
            goto Continue;
        }
        rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_ACKTIMERRANGE;

        // Parse ",<retransmissionatts_range>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseRange(szRsp, rgrcrlp[nUsed].rrRetransmissionAttsRange, szRsp)) {
            goto Continue;
        }
        rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_RETRANSMISSIONATTSRANGE;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp)) {

            if (*szRsp=='(') szRsp++;    // Ignore '(' and ')' characters around version

            // Parse "<version>"
            if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
                goto Continue;
            }

            if (*szRsp==')') szRsp++;    // Ignore '(' and ')' characters around version

            rgrcrlp[nUsed].dwVersion = nValue;
            rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_VERSION;

            // Parse ","
            if ((nValue>1) && (MatchStringBeginning(szRsp, ",", szRsp))) {
                // Parse "<reseqperiod_range>"
                if (!ParseRange(szRsp, rgrcrlp[nUsed].rrReseqPeriodRange, szRsp)) {
                    goto Continue;
                }
                rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_RESEQPERIODRANGE;
            }
        }

        // If we didn't see the version, assume 0 (per GSM 07.07, ch6.8)
        if (!(rgrcrlp[nUsed].dwParams & RIL_PARAM_CRLP_VERSION)) {
            rgrcrlp[nUsed].dwVersion = 0;
            rgrcrlp[nUsed].dwParams |= RIL_PARAM_CRLP_VERSION;
        }

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)rgrcrlp;
    cbBlob = nUsed * sizeof(RILCAPSRLP);

Error:
    if (FAILED(hr)) {
        FreeBlob(rgrcrlp);
    }
    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsEquipmentStates(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsEquipmentStates);
    return ParseRangeListRsp("+CFUN: ", g_rglvmCapsEqStates, NUM_CAPSEQSTATES, TRUE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsPBStoreLocs(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsPBStoreLocs);
    return ParseStringListRsp("+CPBS: ", g_rglsmCapsPBLocations, NUM_CAPSPBLOCS, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsPBIndexRange(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsPBIndexRange);
    HRESULT hr = ParseRangeRsp("+CPBR: ", TRUE, szRsp, pBlob, cbBlob);
#if defined (OEM1_DRIVER) || defined(OEM2_DRIVER)
	if (SUCCEEDED(hr)) {
		RILRANGE * pRange = (RILRANGE*)pBlob;
		if (255 == pRange->dwMaxValue) {
			--(pRange->dwMaxValue);
		}
	}
#endif
	return hr;
}


//
//
//
static HRESULT ParseGetDevCapsPBEntryLength(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsPBEntryLength);
    RILCAPSPBENTRYLENGTH* prcpbel = NULL;
    UINT nValue;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcpbel = (RILCAPSPBENTRYLENGTH*)AllocBlob(sizeof(RILCAPSPBENTRYLENGTH));
    if (!prcpbel) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcpbel, 0x00, sizeof(RILCAPSPBENTRYLENGTH));
    prcpbel->cbSize = sizeof(RILCAPSPBENTRYLENGTH);

    // Parse "<prefix>+CPBF: "
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CPBF: ", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<num_length>", not a required value
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcpbel->dwMaxAddressLength = nValue;
        prcpbel->dwParams |= RIL_PARAM_CPBEL_MAXADDRESSLENGTH;
    }

    // Parse "," <text_length>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<text_length>", not a required value
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcpbel->dwMaxTextLength = nValue;
        prcpbel->dwParams |= RIL_PARAM_CPBEL_MAXTEXTLENGTH;
    }

    // Parse <postfix>
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prcpbel;
    cbBlob = sizeof(RILCAPSPBENTRYLENGTH);

Error:
    if (FAILED(hr)) {
        FreeBlob(prcpbel);
    }
    return hr;
}

//
//
//
static HRESULT ParseGetDevCapsMsgSvcTypes(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsMsgSvcTypes);
    return ParseRangeListRsp("+CSMS: ", g_rglvmCapsMsgSvcTypes, NUM_CAPSMSGSVCTYPES, FALSE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsMsgMemoryLocs(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsMsgMemoryLocs);
    RILCAPSMSGMEMORYLOCATIONS* prcmml = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcmml = (RILCAPSMSGMEMORYLOCATIONS*)AllocBlob(sizeof(RILCAPSMSGMEMORYLOCATIONS));
    if (!prcmml) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcmml, 0x00, sizeof(RILCAPSMSGMEMORYLOCATIONS));
    prcmml->cbSize = sizeof(RILCAPSMSGMEMORYLOCATIONS);

    // Parse "<prefix>+CPMS: <readloc_list>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!MatchStringBeginning(szRsp, "+CPMS: ", szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Skip extraneous '(' at beginning if it's there
    if ((szRsp[0]=='(') && (szRsp[1]=='('))
    {
        szRsp++;
    }

    if (!ParseStringList(szRsp, g_rglsmCapsMsgLocations, NUM_CAPSMSGLOCS, prcmml->dwReadLocations, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    prcmml->dwParams |= RIL_PARAM_CMML_READLOCATIONS;

    // Parse ","
    if (MatchStringBeginning(szRsp, ",", szRsp)) {
        // Parse "<writeloc_list>"
        if (!ParseStringList(szRsp, g_rglsmCapsMsgLocations, NUM_CAPSMSGLOCS, prcmml->dwWriteLocations, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        prcmml->dwParams |= RIL_PARAM_CMML_WRITELOCATIONS;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            // Parse "<storeloc_list>"
            if (!ParseStringList(szRsp, g_rglsmCapsMsgLocations, NUM_CAPSMSGLOCS, prcmml->dwStoreLocations, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }
            prcmml->dwParams |= RIL_PARAM_CMML_STORELOCATIONS;
        }
    }

    pBlob = (void*)prcmml;
    cbBlob = sizeof(RILCAPSMSGMEMORYLOCATIONS);

Error:
    if (FAILED(hr)) {
        FreeBlob(prcmml);
    }
    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsBroadcastMsgLangs(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsBroadcastMsgLangs);
#if defined(WAVECOM_DRIVER) || defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER)
    // HW-SPECIFIC: This uses WaveCom-specific data

    DWORD dwLanguages = (RIL_CAPS_DCSLANG_GERMAN | RIL_CAPS_DCSLANG_ENGLISH | RIL_CAPS_DCSLANG_ITALIAN |
                         RIL_CAPS_DCSLANG_FRENCH | RIL_CAPS_DCSLANG_SPANISH | RIL_CAPS_DCSLANG_DUTCH | RIL_CAPS_DCSLANG_SWEDISH |
                         RIL_CAPS_DCSLANG_DANISH | RIL_CAPS_DCSLANG_PORTUGUESE | RIL_CAPS_DCSLANG_FINNISH |
                         RIL_CAPS_DCSLANG_NORWEGIAN | RIL_CAPS_DCSLANG_GREEK | RIL_CAPS_DCSLANG_TURKISH |
                         RIL_CAPS_DCSLANG_HUNGARIAN | RIL_CAPS_DCSLANG_POLISH | RIL_CAPS_DCSLANG_CZECH);

    HRESULT hr = ParseFakeDWord(dwLanguages, pBlob, cbBlob);
#else  // WAVECOM_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // WAVECOM_DRIVER

    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsMsgConfigIndexRange(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsMsgConfigIndexRange);
#ifdef WAVECOM_DRIVER
    // HW-SPECIFIC: This uses WaveCom-specific data

    RILRANGE* prrIndices = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prrIndices = (RILRANGE*)AllocBlob(sizeof(RILRANGE));
    if (!prrIndices) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prrIndices, 0x00, sizeof(RILRANGE));

    prrIndices->dwMinValue = 0;
    prrIndices->dwMaxValue = 0;

    pBlob = (void*)prrIndices;
    cbBlob = sizeof(RILRANGE);

Error:
    if (FAILED(hr)) {
        FreeBlob(prrIndices);
    }
#else  // WAVECOM_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // WAVECOM_DRIVER

    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsMsgStatusValues(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsMsgStatusValues);
    return ParseRangeListRsp("+CMGL: ", g_rglvmCapsMsgStatusValues, NUM_CAPSMSGSTATUSVALUES, FALSE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsPrefOpIndexRange(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsPrefOpIndexRange);
    return ParseRangeRsp("+CPOL: ", TRUE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsLockFacilities(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsLockFacilities);
    HRESULT hr =  ParseStringListRsp("+CLCK: ", g_rglsmCapsLockFacilities, NUM_CAPSLOCKFACILITIES, szRsp, pBlob, cbBlob);
#ifdef RIL_RADIO_RESILIENCE
    if (SUCCEEDED(hr))
    {
        g_cbLastLockCapsSize = cbBlob;
        if (g_pLastLockCaps)
            FreeBlob(g_pLastLockCaps);
        g_pLastLockCaps = AllocBlob(cbBlob);
        if (!g_pLastLockCaps)
        {
            hr = E_OUTOFMEMORY;
            goto Error;
        }
        memcpy(g_pLastLockCaps, pBlob, cbBlob);
    }
Error:
#endif // RIL_RADIO_RESILIENCE
    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsLockPwdLengths(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsLockPwdLengths);
    char szFacility[MAX_PATH];
    UINT nValue;
    UINT i;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCAPSLOCKINGPWDLENGTH* rgrclpl = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CPWD: "
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CPWD: ", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "("
    while (1) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrclpl, sizeof(RILCAPSLOCKINGPWDLENGTH), nUsed, &nAllocated, CAPS_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrclpl[nUsed], 0x00, sizeof(RILCAPSLOCKINGPWDLENGTH));
        rgrclpl[nUsed].cbSize = sizeof(RILCAPSLOCKINGPWDLENGTH);

        // Parse "(<facility>,<pwd_length>)"
        if (!MatchStringBeginning(szRsp, "(", szRsp)         ||
            !ParseString(szRsp, szFacility, MAX_PATH, szRsp) ||
            !MatchStringBeginning(szRsp, ",", szRsp)         ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)           ||
            !MatchStringBeginning(szRsp, ")", szRsp)) {
            goto Continue;
        }

        for (i = 0; i < NUM_LOCKFACILITIES; i++) {
            if (!strcmp(szFacility, g_rgszLockFacilities[i])) {
                rgrclpl[nUsed].dwFacility = i;
                rgrclpl[nUsed].dwPasswordLength = nValue;
                rgrclpl[nUsed].dwParams |= (RIL_PARAM_CLPL_FACILITY | RIL_PARAM_CLPL_PASSWORDLENGTH);
                break;
            }
        }
        if (NUM_LOCKFACILITIES == i) {
            // We couldn't match the response with anything
            goto Continue;
        }

        // Increment the array index
        nUsed++;

Continue:
        // Parse ","
        if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
            break;
        }
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)rgrclpl;
    cbBlob = nUsed * sizeof(RILCAPSLOCKINGPWDLENGTH);

Error:
    if (FAILED(hr)) {
        FreeBlob(rgrclpl);
    }
    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsBarrTypes(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsBarrTypes);
    return ParseStringListRsp("+CLCK: ", g_rglsmCapsBarrTypes, NUM_CAPSBARRTYPES, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsBarrPwdLengths(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsBarrPwdLengths);
    char szType[MAX_PATH];
    UINT nValue;
    UINT i;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCAPSBARRINGPWDLENGTH* rgrcbpl = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CPWD: "
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CPWD: ", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "("
    while (1) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrcbpl, sizeof(RILCAPSBARRINGPWDLENGTH), nUsed, &nAllocated, CAPS_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrcbpl[nUsed], 0x00, sizeof(RILCAPSBARRINGPWDLENGTH));
        rgrcbpl[nUsed].cbSize = sizeof(RILCAPSBARRINGPWDLENGTH);

        // Parse "(<facility>,<pwd_length>)"
        if (!MatchStringBeginning(szRsp, "(", szRsp)     ||
            !ParseString(szRsp, szType, MAX_PATH, szRsp) ||
            !MatchStringBeginning(szRsp, ",", szRsp)     ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)       ||
            !MatchStringBeginning(szRsp, ")", szRsp)) {
            goto Continue;
        }

        for (i = 0; i < NUM_BARRINGTYPES; i++) {
            if (!strcmp(szType, g_rgszBarringTypes[i])) {
                rgrcbpl[nUsed].dwType = i;
                rgrcbpl[nUsed].dwPasswordLength = nValue;
                rgrcbpl[nUsed].dwParams |= (RIL_PARAM_CBPL_TYPE | RIL_PARAM_CBPL_PASSWORDLENGTH);
                break;
            }
        }
        if (NUM_BARRINGTYPES == i) {
            // We couldn't match the response with anything
            goto Continue;
        }

        // Increment the array index
        nUsed++;

Continue:
        // Parse ","
        if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
            break;
        }
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)rgrcbpl;
    cbBlob = nUsed * sizeof(RILCAPSBARRINGPWDLENGTH);

Error:
    if (FAILED(hr)) {
        FreeBlob(rgrcbpl);
    }
    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsFwdReasons(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsFwdReasons);
    return ParseRangeListRsp("+CCFC: ", g_rglvmCapsFwdReasons, NUM_CAPSFWDREASONS, FALSE, szRsp, pBlob, cbBlob);
}


//
//
//
static HRESULT ParseGetDevCapsInfoClasses(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsInfoClasses);
#if defined(WAVECOM_DRIVER) || defined(OEM1_DRIVER) || defined(OEM2_DRIVER)
    // HW-SPECIFIC: This uses WaveCom-specific data

    HRESULT hr = ParseFakeDWord(RIL_CAPS_INFOCLASS_VOICE | RIL_CAPS_INFOCLASS_DATA | RIL_CAPS_INFOCLASS_FAX, pBlob, cbBlob);
#elif defined(EMP_DRIVER)
    // HW-SPECIFIC: EMP does not support FAX
    HRESULT hr = ParseFakeDWord(RIL_CAPS_INFOCLASS_VOICE | RIL_CAPS_INFOCLASS_DATA, pBlob, cbBlob);
#else  // WAVECOM_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // WAVECOM_DRIVER

    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsHSCSD(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsHSCSD);
    RILCAPSHSCSD* prchscsd = NULL;
    UINT nValue;
    UINT i;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prchscsd = (RILCAPSHSCSD*)AllocBlob(sizeof(RILCAPSHSCSD));
    if (!prchscsd) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prchscsd, 0x00, sizeof(RILCAPSHSCSD));
    prchscsd->cbSize = sizeof(RILCAPSHSCSD);

    // Parse "<prefix>+CHSD: <multislot_class>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CHSD: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prchscsd->dwMultislotClass = nValue;
    prchscsd->dwParams |= RIL_PARAM_CHSCSD_MULTISLOTCLASS;

    // Parse ",<max_rx_timeslots>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prchscsd->dwMaxRxTimeslots = nValue;
    prchscsd->dwParams |= RIL_PARAM_CHSCSD_MAXRXTIMESLOTS;

    // Parse ",<max_tx_timeslots>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prchscsd->dwMaxTxTimeslots = nValue;
    prchscsd->dwParams |= RIL_PARAM_CHSCSD_MAXTXTIMESLOTS;

    // Parse ",<max_timeslots>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prchscsd->dwMaxTotalTimeslots = nValue;
    prchscsd->dwParams |= RIL_PARAM_CHSCSD_MAXTOTALTIMESLOTS;

    // Parse ",<codings><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)   ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Set all required channel coding bits
    if (!nValue) {
        prchscsd->dwChannelCodings = RIL_HSCSDCODING_ALL;
    } else {
        for (i = 0 ; i < NUM_CAPSHSCSDCODINGS; i++) {
            if (nValue & (0x01 << i)) {
                prchscsd->dwChannelCodings |= g_rgdwCapsHSCSDCodings[i];
            }
        }
    }
    prchscsd->dwParams |= RIL_PARAM_CHSCSD_CHANNELCODINGS;


    // Parse "<prefix>+CHSN: <aiur_list>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CHSN: ", szRsp) ||
        !ParseRangeList(szRsp, g_rglvmHSCSDAirRates, NUM_CAPSHSCSDAIRRATES, prchscsd->dwAirInterfaceUserRates,
                        szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prchscsd->dwParams |= RIL_PARAM_CHSCSD_AIRINTERFACEUSERRATES;

    // Parse ",<junk>,<top_rx_timeslot_range>,<junk><postfix>"
    if (!MatchStringBeginning(szRsp, ",", szRsp)                  ||
        !MatchStringAnywhere(szRsp, ",", szRsp)                   ||
        !ParseRange(szRsp, prchscsd->rrTopRxTimeslotRange, szRsp) ||
        !FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prchscsd->dwParams |= RIL_PARAM_CHSCSD_TOPRXTIMESLOTRANGE;

    pBlob = (void*)prchscsd;
    cbBlob = sizeof(RILCAPSHSCSD);

Error:
    if (FAILED(hr)) {
        FreeBlob(prchscsd);
    }
    return hr;
}


//
//
//
static HRESULT ParseGetDevCapsSimToolkitNotifications(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsSimToolkitNotifications);
#if defined(OEM1_DRIVER) || defined(OEM2_SIMTOOLKIT)||defined(WAVECOM_DRIVER)
    // HW-SPECIFIC: This uses OEM1-specific data
    RILSIMTOOLKITNOTIFYCAPS *prstnc = NULL;
    RILSIMTOOLKITEVENTCAPS *prstec = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prstnc = (RILSIMTOOLKITNOTIFYCAPS *)AllocBlob(sizeof(RILSIMTOOLKITNOTIFYCAPS));
    if (!prstnc) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    memset(prstnc, 0x00, sizeof(RILSIMTOOLKITNOTIFYCAPS));
    prstnc->cbSize              = sizeof(RILSIMTOOLKITNOTIFYCAPS);
    prstnc->dwParams            = RIL_PARAM_SIMTKN_ALL;
    prstnc->dwMoreTime          = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
    prstnc->dwPollInterval      = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
    prstnc->dwPollingOff        = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
#if defined(OEM2_SIMTOOLKIT)||defined(WAVECOM_DRIVER)                                                                       //Modify by zxn
    prstnc->dwSetUpCall         = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;
#else
    prstnc->dwSetUpCall         = RIL_SIMTKN_RADIOIMPLEMENTS_REQUESTMEINPUT;
#endif
    prstnc->dwSendSS            = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;
    prstnc->dwSendUSSD          = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;
    prstnc->dwSendSMS           = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;
    prstnc->dwPlayTone          = RIL_SIMTKN_MEIMPLEMENTS;
    prstnc->dwDisplayText       = RIL_SIMTKN_MEIMPLEMENTS;
    prstnc->dwGetInkey          = RIL_SIMTKN_MEIMPLEMENTS;
    prstnc->dwGetInput          = RIL_SIMTKN_MEIMPLEMENTS;
    prstnc->dwSelectItem        = RIL_SIMTKN_MEIMPLEMENTS;
    prstnc->dwSetupMenu         = RIL_SIMTKN_MEIMPLEMENTS;
#if defined(WAVECOM_DRIVER)    
    prstnc->dwRefresh           = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;
#else  
    prstnc->dwRefresh           = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
#endif
    prstnc->dwSetupIdleModeText = RIL_SIMTKN_MEIMPLEMENTS;
    prstnc->dwLocalInfo         = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
    prstnc->dwSetupEventList    = RIL_SIMTKN_MEIMPLEMENTS;
#ifdef OEM2_SIMTOOLKIT
    prstnc->dwSendDTMF          = RIL_SIMTKN_RADIOIMPLEMENTS_REQUESTMEINPUT;
#else
    prstnc->dwSendDTMF          = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;
#endif
    prstnc->dwSendDTMF          = RIL_SIMTKN_RADIOIMPLEMENTS_REQUESTMEINPUT;
    prstnc->dwLaunchBrowser     = RIL_SIMTKN_MEIMPLEMENTS;
    prstnc->dwOpenChannel       = RIL_SIMTKN_RADIOIMPLEMENTS_REQUESTMEINPUT;
    prstnc->dwCloseChannel      = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;
    prstnc->dwReceiveData       = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;
    prstnc->dwSendData          = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;
    prstnc->dwTimerManagement   = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
    prstnc->dwRunAtCmd          = RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION;

    prstnc->dwNotifyFlags       = (RIL_CAPS_NOTIFY_SIMTOOLKITCMD       |
                                   RIL_CAPS_NOTIFY_SIMTOOLKITCALLSETUP |
                                   RIL_CAPS_NOTIFY_SIMTOOLKITEVENT);

    prstec = &(prstnc->rstecEvents);
    prstec->cbSize               = sizeof(RILSIMTOOLKITEVENTCAPS);
    prstec->dwParams             = RIL_PARAM_SIMTKE_ALL;
    prstec->dwMTCall             = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
    prstec->dwCallConnected      = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
    prstec->dwCallDisconnected   = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
    prstec->dwLocationStatus     = RIL_SIMTKN_RADIOIMPLEMENTS_NONOTIFICATION;
    prstec->dwUserActivity       = RIL_SIMTKN_MEIMPLEMENTS;
    prstec->dwIdleScreen         = RIL_SIMTKN_MEIMPLEMENTS;
    prstec->dwLanguageSelection  = RIL_SIMTKN_MEIMPLEMENTS;
    prstec->dwBrowserTermination = RIL_SIMTKN_NOSUPPORT;
    prstec->dwDataAvailable      = RIL_SIMTKN_NOSUPPORT;
    prstec->dwChannelStatus      = RIL_SIMTKN_NOSUPPORT;
    prstec->dwDisplayChange      = RIL_SIMTKN_NOSUPPORT;

    pBlob = (void*)prstnc;
    cbBlob = sizeof(RILSIMTOOLKITNOTIFYCAPS);

Error:
    if (FAILED(hr)) {
        FreeBlob(prstnc);
    }

#else  // defined(OEM1_DRIVER) || defined(OEM2_SIMTOOLKIT)

    HRESULT hr = E_NOTIMPL;
#endif // defined(OEM1_DRIVER) || defined(OEM2_SIMTOOLKIT)

    return hr;
}

//
//
//
static HRESULT ParseGetSignalQualityImplemenation(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetSignalQualityImplemenation);
    HRESULT hr = S_OK;
    DWORD * pdwSignalQualityImplCaps = NULL;

    pBlob = NULL;
    cbBlob = 0;

    pdwSignalQualityImplCaps = (DWORD *)AllocBlob(sizeof(DWORD));
    if (!pdwSignalQualityImplCaps) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

#if defined(OEM1_CSQ_NOTIFICATION) || defined(OEM2_DRIVER) || defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
    // OEM2 supports signal quality notifications.
    *pdwSignalQualityImplCaps = RIL_CAPS_SIGNALQUALITY_NOTIFICATION;
#else
    // Currently, all other radios support polling only.
    *pdwSignalQualityImplCaps = RIL_CAPS_SIGNALQUALITY_POLLING;
#endif
    pBlob = (void*)pdwSignalQualityImplCaps;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwSignalQualityImplCaps);
    }

    return hr;
}

//
//
//
static HRESULT ParseGetDevCapsSIMSupport(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetDevCapsSIMSupport);
    HRESULT hr = S_OK;
    DWORD * pdwSimSupportCaps = NULL;

    pBlob = NULL;
    cbBlob = 0;

    pdwSimSupportCaps = (DWORD *)AllocBlob(sizeof(DWORD));
    if (!pdwSimSupportCaps) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    // GSM devices have SIM support.
    *pdwSimSupportCaps = RIL_CAPS_SIM_BASIC;

    pBlob = (void*)pdwSimSupportCaps;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwSimSupportCaps);
    }

    return hr;
}



//
//
//
static HRESULT ParseGetCallProgressNotification(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    TBD_FUNCTION(ParseGetCallProgressNotification);
    HRESULT hr = S_OK;
    DWORD * pdwCallProgressNotification = NULL;

    pBlob = NULL;
    cbBlob = 0;

    pdwCallProgressNotification = (DWORD *)AllocBlob(sizeof(DWORD));
    if (!pdwCallProgressNotification) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER)
    || defined(WAVECOM_DRIVER)  /* added by Gao, renhui@2007-04-02 for CEDB200050585. */
    // OEM1 supports call progress notifications, adding OEM2 and EMP
    *pdwCallProgressNotification = true;
#else
    *pdwCallProgressNotification = false;
#endif

    pBlob = (void*)pdwCallProgressNotification;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwCallProgressNotification);
    }

    return hr;
}

#if defined(OEM1_DRIVER)
static HRESULT ParseGetNITZSupported(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetNITZSupported);
    UINT nValue;
    HRESULT hr = E_FAIL;
    DWORD * pdwNITZNotificationSetting = NULL;


    pBlob = NULL;
    cbBlob = 0;

    pdwNITZNotificationSetting = (DWORD *)AllocBlob(sizeof(DWORD));
    if (!pdwNITZNotificationSetting) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        goto Error;
    }

    // Parse "%CTZV: "
    if (MatchStringBeginning(szRsp, "%CTZV: ", szRsp)) 
    {
        if (ParseUInt(szRsp, FALSE, nValue, szRsp))
        {
            hr = S_OK;
            if ( 0 == nValue )
            {
                *pdwNITZNotificationSetting = RIL_CAPS_NITZ_DISABLED;
            }
            else
            {
                *pdwNITZNotificationSetting = RIL_CAPS_NITZ_ENABLED;
            }
            pBlob = (void*)pdwNITZNotificationSetting;
            cbBlob = sizeof(DWORD);
        }
    }

Error:
    if (FAILED(hr)) {
        FreeBlob(pdwNITZNotificationSetting);
    }
    return hr;
}
#endif
//
//
//
HRESULT RILDrv_GetDevCaps(DWORD dwParam, DWORD dwCapsType)
{
    FUNCTION_TRACE(RILDrv_GetDevCaps);
    HRESULT hr = S_OK;
    LPCSTR szCmd = NULL;
    DWORD dwOptions = CMDOPT_NONE;
    PFN_CMD_PARSE pfnParse = NULL;
    BOOL fSpoofed = FALSE;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    switch (dwCapsType)
    {
        case RIL_CAPSTYPE_DIAL:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsDial;
            break;

        case RIL_CAPSTYPE_DTMFDURATIONRANGE:
#ifdef EMP_DRIVER
            hr = E_NOTIMPL;
            goto Error;
#else
            szCmd = "AT+VTD=?\r";
            pfnParse = ParseGetDevCapsDTMFDurationRange;
            break;
#endif
        case RIL_CAPSTYPE_CALLMGTCMDS:
            szCmd = "AT+CHLD=?\r";
            pfnParse = ParseGetDevCapsCallMgtCmds;
            break;

        case RIL_CAPSTYPE_BEARERSERVICE:
            szCmd = "AT+CBST=?\r";
            pfnParse = ParseGetDevCapsBearerSvc;
            break;

        case RIL_CAPSTYPE_RLP:
            szCmd = "AT+CRLP=?\r";
            pfnParse = ParseGetDevCapsRLP;
            break;

        case RIL_CAPSTYPE_EQUIPMENTSTATES:
            szCmd = "AT+CFUN=?\r";
            pfnParse = ParseGetDevCapsEquipmentStates;
            break;

        case RIL_CAPSTYPE_PBSTORELOCATIONS:
            szCmd = "AT+CPBS=?\r";
            pfnParse = ParseGetDevCapsPBStoreLocs;
            break;

        case RIL_CAPSTYPE_PBINDEXRANGE:
            szCmd = "AT+CPBR=?\r";
            pfnParse = ParseGetDevCapsPBIndexRange;
            break;

        case RIL_CAPSTYPE_PBENTRYLENGTH:
            szCmd = "AT+CPBF=?\r";
            pfnParse = ParseGetDevCapsPBEntryLength;
            break;

        case RIL_CAPSTYPE_MSGSERVICETYPES:
            szCmd = "AT+CSMS=?\r";
            pfnParse = ParseGetDevCapsMsgSvcTypes;
            break;

        case RIL_CAPSTYPE_MSGMEMORYLOCATIONS:
            szCmd = "AT+CPMS=?\r";
            pfnParse = ParseGetDevCapsMsgMemoryLocs;
            break;

        case RIL_CAPSTYPE_BROADCASTMSGLANGS:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsBroadcastMsgLangs;
            break;

        case RIL_CAPSTYPE_MSGCONFIGINDEXRANGE:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsMsgConfigIndexRange;
            break;

        case RIL_CAPSTYPE_MSGSTATUSVALUES:
            szCmd = "AT+CMGL=?\r";
            pfnParse = ParseGetDevCapsMsgStatusValues;
            break;

        case RIL_CAPSTYPE_PREFOPINDEXRANGE:
            szCmd = "AT+CPOL=?\r";
            pfnParse = ParseGetDevCapsPrefOpIndexRange;
            break;

        case RIL_CAPSTYPE_LOCKFACILITIES:
#ifdef RIL_RADIO_RESILIENCE
            if (ShouldSpoofCommand() && g_cbLastLockCapsSize)
            {
                fSpoofed = TRUE;
                void *pLockCaps = AllocBlob(g_cbLastLockCapsSize);
                if (!pLockCaps) {
                    hr = E_OUTOFMEMORY;
                    goto Error;
                }
                hr = pHandle->GetDevice()->GetNextCmdID();
                memcpy(pLockCaps, g_pLastLockCaps, g_cbLastLockCapsSize);
                pHandle->Notify(RIL_RESULT_OK, hr, pLockCaps, g_cbLastLockCapsSize);
            }
            else
#endif
            {
                szCmd = "AT+CLCK=?\r";
                pfnParse = ParseGetDevCapsLockFacilities;
            }
            break;

        case RIL_CAPSTYPE_LOCKINGPWDLENGTHS:
            szCmd = "AT+CPWD=?\r";
            pfnParse = ParseGetDevCapsLockPwdLengths;
            break;

        case RIL_CAPSTYPE_BARRTYPES:
            szCmd = "AT+CLCK=?\r";
            pfnParse = ParseGetDevCapsBarrTypes;
            break;

        case RIL_CAPSTYPE_BARRINGPWDLENGTHS:
            szCmd = "AT+CPWD=?\r";
            pfnParse = ParseGetDevCapsBarrPwdLengths;
            break;

        case RIL_CAPSTYPE_FORWARDINGREASONS:
            szCmd = "AT+CCFC=?\r";
            pfnParse = ParseGetDevCapsFwdReasons;
            break;

        case RIL_CAPSTYPE_INFOCLASSES:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsInfoClasses;
            break;

        case RIL_CAPSTYPE_HSCSD:
#ifdef WAVECOM_DRIVER
            // HW-SPECIFIC: WaveCom hardware doesn't support AT+CHSD and AT+CHSN

            hr = E_NOTIMPL;
            goto Error;
#else  // WAVECOM_DRIVER

            szCmd = "AT+CHSD;+CHSN=?\r";
            pfnParse = ParseGetDevCapsHSCSD;
            break;
#endif // WAVECOM_DRIVER

        case RIL_CAPSTYPE_SIMTOOLKITNOTIFICATIONS:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsSimToolkitNotifications;
            break;


        case RIL_CAPSTYPE_GPRSCLASS:
#ifdef EMP_DRIVER
            // HW-SPECIFIC: EMP does not support +CGCLASS
            hr = E_NOTIMPL;
            goto Error;
#else  // WAVECOM_DRIVER
            szCmd = "AT+CGCLASS=?\r";
            pfnParse = ParseGetGprsCapsClass;
            break;
#endif
        case RIL_CAPSTYPE_GPRSCONTEXT:
            szCmd = "AT+CGDCONT=?\r";
            pfnParse = ParseGetGprsCapsContext;
            break;
        case RIL_CAPSTYPE_GPRSQOS:
            szCmd = "AT+CGQREQ=?\r";
            pfnParse = ParseGetGprsCapsQOS;
            break;
        case RIL_CAPSTYPE_GPRSQOSMIN:
            szCmd = "AT+CGQMIN=?\r";
            pfnParse = ParseGetGprsCapsQOSMin;
            break;
        case RIL_CAPSTYPE_GPRSMOSMS:
            szCmd = "AT+CGSMS=?\r";
            pfnParse = ParseGetGprsCapsSMOSMS;
            break;

        case RIL_CAPSTYPE_DATACOMPRESSION:
            szCmd = "AT+DS=?\r";
            pfnParse = ParseGetDevCapsDataCompression;
            break;
        case RIL_CAPSTYPE_ERRORCORRECTION:
#ifdef EMP_DRIVER
            hr = E_NOTIMPL;
            goto Error;
#else
            szCmd = "AT+ES=?\r";
            pfnParse = ParseGetDevCapsErrorCorrection;
            break;
#endif
        case RIL_CAPSTYPE_SIGNALQUALITYIMPLEMENTATION:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetSignalQualityImplemenation;
            break;

        case RIL_CAPSTYPE_SIMSUPPORT:
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
            pfnParse = ParseGetDevCapsSIMSupport;
            break;

        case RIL_CAPSTYPE_CALLPROGRESSNOTIFICATION:
//#ifdef OEM1_DRIVER                // may want to actually ask in the future - for now assume all OEM1 radios support this
//            szCmd = "AT+CPI?\r";
//#else
            szCmd = NULL;
            dwOptions = CMDOPT_NOOP;
//#endif
            pfnParse = ParseGetCallProgressNotification;
            break;

        case RIL_CAPSTYPE_NITZNOTIFICATION:
#ifdef OEM1_DRIVER                // may want to actually ask in the future - for now assume all OEM1 radios support this
            szCmd = "AT%CTZV?\r";
            pfnParse = ParseGetNITZSupported;
#else
            hr = E_NOTIMPL;
            goto Error;
#endif
            break;


        default:
            hr = E_INVALIDARG;
            goto Error;
    }

    if (!fSpoofed)
        if (!QueueCmd(pHandle, szCmd, dwOptions, APIID_GETDEVCAPS, pfnParse, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }

Error:
    return hr;
}
